package com.ruoyi.web.controller.system;

import cn.hutool.core.bean.BeanUtil;
import com.angel.mongodb.utils.Wraps;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.service.impl.BaseService;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.web.viewObject.SysMenuVo;
import com.ruoyi.web.viewObject.TreeSelect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: 菜单信息
 * @author: gankench@gmail.com
 * @time: 4/10/21 9:20 PM
 */
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
    @Autowired
    private BaseService baseService;

    @Autowired
    private TokenService tokenService;

    /**
     * 获取菜单列表
     */
    @PreAuthorize("@ss.hasPermi('system:menu:list')")
    @GetMapping("/list")
    public AjaxResult list(SysMenuVo menu) {
        SysMenu sysMenu = BeanUtil.copyProperties(menu, SysMenu.class);
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUser user = loginUser.getUser();
        List<SysMenu> menus = Collections.emptyList();
        if (user.isAdmin()) {
            menus = baseService.findListByQuery(Wraps.buildBaseQuery(sysMenu), SysMenu.class);
        } else {
            List<String> roles = baseService.findPropertiesByQuery(Wraps.andWrapper().eq(SysUser::getId, user.getId()), SysUser.class, SysUser::getRoleIds);

            List<String> menuIds = baseService.findPropertiesByQuery(Wraps.andWrapper().in(SysRole::getId, roles), SysRole.class, SysRole::getMenuIds);
            menus = baseService.findListByIds(menuIds, SysMenu.class);
        }
        return AjaxResult.success(menus);
    }

    /**
     * 根据菜单编号获取详细信息
     */
    @PreAuthorize("@ss.hasPermi('system:menu:query')")
    @GetMapping(value = "/{menuId}")
    public AjaxResult getInfo(@PathVariable String menuId) {
        return AjaxResult.success(baseService.findById(menuId, SysMenu.class));
    }

    /**
     * 获取菜单下拉树列表
     */
    @GetMapping("/treeselect")
    public AjaxResult treeselect(SysMenu menu) {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        String userId = loginUser.getUser().getId().toString();
//        List<SysMenu> menus = menuService.selectMenuList(menu, userId);
//        return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
        return AjaxResult.error();
    }

    /**
     * 加载对应角色菜单列表树
     */
    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
    public AjaxResult roleMenuTreeselect(@PathVariable("roleId") String roleId) {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUser user = loginUser.getUser();
        AjaxResult ajax = AjaxResult.success();
        List<SysMenuVo> menus = Collections.emptyList();
        List menuIds = baseService.findPropertiesByQuery(Wraps.andWrapper().eq(SysRole::getId, roleId), SysRole.class, SysRole::getMenuIds);
        if (user.isAdmin()) {
            menus = baseService.findListByQuery(Wraps.andWrapper(), SysMenu.class)
                    .stream()
                    .map(e -> BeanUtil.copyProperties(e, SysMenuVo.class))
                    .collect(Collectors.toList());
        } else {
            menus = baseService.findListByIds((List<String>) menuIds, SysMenu.class)
                    .stream()
                    .map(e -> BeanUtil.copyProperties(e, SysMenuVo.class))
                    .collect(Collectors.toList());
        }

        ajax.put("checkedKeys", menuIds);
        ajax.put("menus", buildMenuTreeSelect(menus));
        return ajax;
    }

    /**
     * 新增菜单
     */
    @PreAuthorize("@ss.hasPermi('system:menu:add')")
    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysMenu menu) {
        if (UserConstants.NOT_UNIQUE.equals(baseService.findCountByQuery(Wraps.andWrapper(menu), SysMenu.class))) {
            return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
                && !StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS)) {
            return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败，地址必须以http(s)://开头");
        }
        menu.setCreateBy(SecurityUtils.getUsername());
        return toAjax(baseService.save(menu));
    }

    /**
     * 修改菜单
     */
    @PreAuthorize("@ss.hasPermi('system:menu:edit')")
    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysMenu menu) {
        if (UserConstants.NOT_UNIQUE.equals(baseService.findCountByQuery(Wraps.andWrapper(menu), SysMenu.class))) {
            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
                && !StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS)) {
            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败，地址必须以http(s)://开头");
        } else if (menu.getId().equals(menu.getParentId())) {
            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败，上级菜单不能选择自己");
        }
        menu.setUpdateBy(SecurityUtils.getUsername());
        baseService.updateById(menu);
        return toAjax(1);
    }

    /**
     * 删除菜单
     */
    @PreAuthorize("@ss.hasPermi('system:menu:remove')")
    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{menuId}")
    public AjaxResult remove(@PathVariable("menuId") String menuId) {
        if (hasChildByMenuId(menuId)) {
            return AjaxResult.error("存在子菜单,不允许删除");
        }
        if (checkMenuExistRole(menuId)) {
            return AjaxResult.error("菜单已分配,不允许删除");
        }
        baseService.deleteById(menuId, SysMenu.class);
        return toAjax(1);
    }

    /**
     * 是否存在菜单子节点
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    private boolean hasChildByMenuId(String menuId) {
        Long result = baseService.findCountByQuery(Wraps.andWrapper().eq(SysMenu::getParentId, menuId), SysMenu.class);
        return result > 0L ? true : false;
    }

    /**
     * 查询菜单使用数量
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    private boolean checkMenuExistRole(String menuId) {
        Long result = baseService.findCountByQuery(Wraps.andWrapper().contain(SysRole::getMenuIds, menuId), SysRole.class);
        return result > 0L ? true : false;
    }

    /**
     * 构建前端所需要树结构
     *
     * @param menus 菜单列表
     * @return 树结构列表
     */
    public List<SysMenuVo> buildMenuTree(List<SysMenuVo> menus) {
        List<SysMenuVo> returnList = new ArrayList<SysMenuVo>();
        List<String> tempList = new ArrayList<String>();
        for (SysMenuVo dept : menus) {
            tempList.add(dept.getId());
        }
        for (Iterator<SysMenuVo> iterator = menus.iterator(); iterator.hasNext(); ) {
            SysMenuVo menu = (SysMenuVo) iterator.next();
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(menu.getParentId())) {
                recursionFn(menus, menu);
                returnList.add(menu);
            }
        }
        if (returnList.isEmpty()) {
            returnList = menus;
        }
        return returnList;
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param menus 菜单列表
     * @return 下拉树结构列表
     */
    public List<TreeSelect> buildMenuTreeSelect(List<SysMenuVo> menus) {
        List<SysMenuVo> menuTrees = buildMenuTree(menus);
        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 递归列表
     *
     * @param list
     * @param t
     */
    private void recursionFn(List<SysMenuVo> list, SysMenuVo t) {
        // 得到子节点列表
        List<SysMenuVo> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysMenuVo tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysMenuVo> getChildList(List<SysMenuVo> list, SysMenuVo t) {
        List<SysMenuVo> tlist = new ArrayList<SysMenuVo>();
        Iterator<SysMenuVo> it = list.iterator();
        while (it.hasNext()) {
            SysMenuVo n = (SysMenuVo) it.next();
            if (t.getId().equalsIgnoreCase(n.getParentId())) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenuVo> list, SysMenuVo t) {
        return getChildList(list, t).size() > 0 ? true : false;
    }
}